In this workshop, we will explore how to create tables using the gt and gtExtras packages, utilizing the pizza place dataset. We will start with a basic table using base R to display pizza information, then enhance our visualization using the gt package to create a more polished and user-friendly table. Finally, we will upgrade our visualization further with the gtExtras package, adding even more features and styles to improve the overall presentation of our pizza menu.
Let’s look at our pizza sales dataset:
# Load and assign the pizza dataset to an object
data("pizzaplace")
pizza_data <- pizzaplace
head(pizza_data)
## # A tibble: 6 × 7
## id date time name size type price
## <chr> <chr> <chr> <chr> <chr> <chr> <dbl>
## 1 2015-000001 2015-01-01 11:38:36 hawaiian M classic 13.2
## 2 2015-000002 2015-01-01 11:57:40 classic_dlx M classic 16
## 3 2015-000002 2015-01-01 11:57:40 mexicana M veggie 16
## 4 2015-000002 2015-01-01 11:57:40 thai_ckn L chicken 20.8
## 5 2015-000002 2015-01-01 11:57:40 five_cheese L veggie 18.5
## 6 2015-000002 2015-01-01 11:57:40 ital_supr L supreme 20.8
First, let’s create a summary table using basic R:
pizza_summary <- pizza_data %>%
group_by(type, size) %>%
summarize(
total_sales = sum(price),
num_orders = n(),
avg_price = mean(price),
.groups = 'drop'
)
# Display as basic table
print(pizza_summary)
## # A tibble: 14 × 5
## type size total_sales num_orders avg_price
## <chr> <chr> <dbl> <int> <dbl>
## 1 chicken L 102339 4932 20.8
## 2 chicken M 65224. 3894 16.8
## 3 chicken S 28356 2224 12.8
## 4 classic L 74518. 4057 18.4
## 5 classic M 60582. 4112 14.7
## 6 classic S 69870. 6139 11.4
## 7 classic XL 14076 552 25.5
## 8 classic XXL 1007. 28 36.0
## 9 supreme L 94258. 4564 20.7
## 10 supreme M 66475 4046 16.4
## 11 supreme S 47464. 3377 14.1
## 12 veggie L 104203. 5403 19.3
## 13 veggie M 57101 3583 15.9
## 14 veggie S 32387. 2663 12.2
Let’s start with a simple gt table:
pizza_summary %>%
gt(
groupname_col = "type"
)
| size | total_sales | num_orders | avg_price |
|---|---|---|---|
| chicken | |||
| L | 102339.00 | 4932 | 20.75000 |
| M | 65224.50 | 3894 | 16.75000 |
| S | 28356.00 | 2224 | 12.75000 |
| classic | |||
| L | 74518.50 | 4057 | 18.36788 |
| M | 60581.75 | 4112 | 14.73292 |
| S | 69870.25 | 6139 | 11.38137 |
| XL | 14076.00 | 552 | 25.50000 |
| XXL | 1006.60 | 28 | 35.95000 |
| supreme | |||
| L | 94258.50 | 4564 | 20.65261 |
| M | 66475.00 | 4046 | 16.42981 |
| S | 47463.50 | 3377 | 14.05493 |
| veggie | |||
| L | 104202.70 | 5403 | 19.28608 |
| M | 57101.00 | 3583 | 15.93665 |
| S | 32386.75 | 2663 | 12.16175 |
Now let’s add currency and number formatting:
pizza_summary %>%
gt(
groupname_col = "type"
) %>%
fmt_currency(
columns = c(total_sales, avg_price),
currency = "USD"
) %>%
fmt_number(
columns = num_orders,
decimals = 0
)
| size | total_sales | num_orders | avg_price |
|---|---|---|---|
| chicken | |||
| L | $102,339.00 | 4,932 | $20.75 |
| M | $65,224.50 | 3,894 | $16.75 |
| S | $28,356.00 | 2,224 | $12.75 |
| classic | |||
| L | $74,518.50 | 4,057 | $18.37 |
| M | $60,581.75 | 4,112 | $14.73 |
| S | $69,870.25 | 6,139 | $11.38 |
| XL | $14,076.00 | 552 | $25.50 |
| XXL | $1,006.60 | 28 | $35.95 |
| supreme | |||
| L | $94,258.50 | 4,564 | $20.65 |
| M | $66,475.00 | 4,046 | $16.43 |
| S | $47,463.50 | 3,377 | $14.05 |
| veggie | |||
| L | $104,202.70 | 5,403 | $19.29 |
| M | $57,101.00 | 3,583 | $15.94 |
| S | $32,386.75 | 2,663 | $12.16 |
Let’s improve the presentation with better headers:
pizza_summary %>%
gt(
groupname_col = "type"
) %>%
fmt_currency(
columns = c(total_sales, avg_price),
currency = "USD"
) %>%
fmt_number(
columns = num_orders,
decimals = 0
) %>%
tab_header(
title = "Pizza Sales Analysis",
subtitle = "Sales breakdown by type and size"
) %>%
cols_label(
total_sales = "Total Sales",
num_orders = "Number of Orders",
avg_price = "Average Price",
size = "Pizza Size"
)
| Pizza Sales Analysis | |||
| Sales breakdown by type and size | |||
| Pizza Size | Total Sales | Number of Orders | Average Price |
|---|---|---|---|
| chicken | |||
| L | $102,339.00 | 4,932 | $20.75 |
| M | $65,224.50 | 3,894 | $16.75 |
| S | $28,356.00 | 2,224 | $12.75 |
| classic | |||
| L | $74,518.50 | 4,057 | $18.37 |
| M | $60,581.75 | 4,112 | $14.73 |
| S | $69,870.25 | 6,139 | $11.38 |
| XL | $14,076.00 | 552 | $25.50 |
| XXL | $1,006.60 | 28 | $35.95 |
| supreme | |||
| L | $94,258.50 | 4,564 | $20.65 |
| M | $66,475.00 | 4,046 | $16.43 |
| S | $47,463.50 | 3,377 | $14.05 |
| veggie | |||
| L | $104,202.70 | 5,403 | $19.29 |
| M | $57,101.00 | 3,583 | $15.94 |
| S | $32,386.75 | 2,663 | $12.16 |
Now let’s add some visual enhancements:
pizza_summary %>%
gt(
groupname_col = "type"
) %>%
fmt_currency(
columns = c(total_sales, avg_price),
currency = "USD"
) %>%
fmt_number(
columns = num_orders,
decimals = 0
) %>%
tab_header(
title = "Pizza Sales Analysis",
subtitle = "Sales breakdown by type and size"
) %>%
cols_label(
total_sales = "Total Sales",
num_orders = "Number of Orders",
avg_price = "Average Price",
size = "Pizza Size"
) %>%
gt_theme_538() %>%
data_color(
columns = c(total_sales),
colors = scales::col_numeric(
palette = c("white", "#1F77B4"),
domain = NULL
)
)
| Pizza Sales Analysis | |||
| Sales breakdown by type and size | |||
| Pizza Size | Total Sales | Number of Orders | Average Price |
|---|---|---|---|
| chicken | |||
| L | $102,339.00 | 4,932 | $20.75 |
| M | $65,224.50 | 3,894 | $16.75 |
| S | $28,356.00 | 2,224 | $12.75 |
| classic | |||
| L | $74,518.50 | 4,057 | $18.37 |
| M | $60,581.75 | 4,112 | $14.73 |
| S | $69,870.25 | 6,139 | $11.38 |
| XL | $14,076.00 | 552 | $25.50 |
| XXL | $1,006.60 | 28 | $35.95 |
| supreme | |||
| L | $94,258.50 | 4,564 | $20.65 |
| M | $66,475.00 | 4,046 | $16.43 |
| S | $47,463.50 | 3,377 | $14.05 |
| veggie | |||
| L | $104,202.70 | 5,403 | $19.29 |
| M | $57,101.00 | 3,583 | $15.94 |
| S | $32,386.75 | 2,663 | $12.16 |
Let’s create a different view focusing on pizza types:
pizza_data %>%
group_by(type) %>%
summarize(
total_sales = sum(price),
num_orders = n(),
avg_price = mean(price),
pct_total = num_orders/nrow(pizza_data)
) %>%
ungroup() %>%
gt() %>%
fmt_currency(
columns = c(total_sales, avg_price),
currency = "USD"
) %>%
fmt_number(
columns = num_orders,
decimals = 0
) %>%
fmt_percent(
columns = pct_total,
decimals = 1
) %>%
data_color(
columns = pct_total,
colors = scales::col_numeric(
palette = c("white", "steelblue"),
domain = NULL
)
) %>%
cols_label(
type = "Pizza Type",
total_sales = "Total Sales",
num_orders = "Number of Orders",
avg_price = "Average Price",
pct_total = "% of Total Orders"
) %>%
tab_header(
title = "Pizza Type Analysis",
subtitle = "Sales and order distribution by pizza type"
)
| Pizza Type Analysis | ||||
| Sales and order distribution by pizza type | ||||
| Pizza Type | Total Sales | Number of Orders | Average Price | % of Total Orders |
|---|---|---|---|---|
| chicken | $195,919.50 | 11,050 | $17.73 | 22.3% |
| classic | $220,053.10 | 14,888 | $14.78 | 30.0% |
| supreme | $208,197.00 | 11,987 | $17.37 | 24.2% |
| veggie | $193,690.45 | 11,649 | $16.63 | 23.5% |
pizza_data %>%
group_by(type) %>%
summarize(
total_sales = sum(price),
num_orders = n(),
avg_price = mean(price)
) %>%
gt() %>%
fmt_currency(
columns = c(total_sales, avg_price),
currency = "USD"
) %>%
fmt_number(
columns = num_orders,
decimals = 0
) %>%
gt_theme_dark() %>%
tab_header(
title = "Pizza Sales in Dark Theme",
subtitle = "A different perspective on our data"
)
| Pizza Sales in Dark Theme | |||
| A different perspective on our data | |||
| type | total_sales | num_orders | avg_price |
|---|---|---|---|
| chicken | $195,919.50 | 11,050 | $17.73 |
| classic | $220,053.10 | 14,888 | $14.78 |
| supreme | $208,197.00 | 11,987 | $17.37 |
| veggie | $193,690.45 | 11,649 | $16.63 |
pizza_data %>%
group_by(type) %>%
summarize(
total_sales = sum(price),
num_orders = n(),
avg_price = mean(price)
) %>%
ungroup() %>%
gt(
groupname_col = "type"
) %>%
fmt_currency(
columns = c(total_sales, avg_price),
currency = "USD"
) %>%
fmt_number(
columns = num_orders,
decimals = 0
) %>%
grand_summary_rows(
columns = total_sales,
fns = list(
"Total" = ~sum(.)
),
fmt = ~ fmt_currency(., currency = "USD")
) %>%
grand_summary_rows(
columns = num_orders,
fns = list(
"Total" = ~sum(.)
),
fmt = ~ fmt_number(., decimals = 0)
)
| total_sales | num_orders | avg_price | |
|---|---|---|---|
| chicken | |||
| $195,919.50 | 11,050 | $17.73 | |
| classic | |||
| $220,053.10 | 14,888 | $14.78 | |
| supreme | |||
| $208,197.00 | 11,987 | $17.37 | |
| veggie | |||
| $193,690.45 | 11,649 | $16.63 | |
| Total | $817,860.05 | 49,574 | — |